Level 0 | Level 1 | Level 2 | Level 3 | Level 4 |
Level 5 | Level 6 | Level 7 | Level 8 | Level 9 |
Level 10 | Level 11 | Level 12 | Level 13 | Level 14 |
Use nes2nsf to extract the bank from hector 87 and the bank extracted will be 005. Go ahead and try to play the rip and
you notice that it will not play. A NSF at these harder levels will never play with the NSF address calls set at 8003/8000
for init and play respectively. So your first task is to find the play address that I showed you in level 5. Before you do
this remove the header and disassemble the game.
This time you don't have to use Nesten to debug because you have already extracted the right bank. Use FCEUD and bring up
the debugger while the music is playing. Set a write break point for $4000 - $4009 and wait for the snap. Write the stack
contents down. Remember you have to flip the bytes and subtract 2. Next you will want to find the NMI interrupt routine.
With FCEUD it's easy. First disable the sound write break points and then type in NMI and check the execute box and click
run and let it snap. The debugger snaps on $DB5E at the start of the NMI. Now if you was paying attention you will have
noted where the music code is and the music driver is located in the $8xxx area of PRG. You want to look for a JSR or JMP
that goes to this area. So first look at Address $DB8E, this was one of the addresses in the contents of the stack. Check
the code there. $DB8E - JMP $8009. So $8009 is the play address. Place this address in the header in the play area.
The play address call is usually the easiest to find. The init address call is another story. The init initializes memory
addresses,sets up data blocks,changes the tune. However you can sometimes find the init address by setting a write break
point to 4015 in an emulator debugger before the music starts. So you write down the stacks contents like you did for the
play and look at your disassembly and try out the addresses in the header and see if they work. Usually this doesn't work.
What you might find most of the time is a sub routine that is a bootstrap code that initializes the sound registers and
that's about it, this will not make the tunes play unless you add it to the init routine that you found. There is a few
different types of init routines and the one we are going to focus on is the type that uses a memory address to switch the
tunes. First I need to explain memory addresses to you.
Memory addresses are used to store numbers and use them again and save them for later whenever the code needs them. So
usually the accumulator contents is transferred to these addresses and the accumulator loads from these addresses as well.
The data in addresses is also manipulated in other ways as well by the code. I'm going to show you the memory map of the
WRAM(Work RAM) page by page.
So the range of WRAM is $0 - $07FF. The entire game uses these addresses and you have to figure out which is used to switch
the tunes. I have a couple of tricks to use to discover this address. Using a memory viewer helps with this and FCEU(not the
D version) and VirtuaNES has a memory viewer. Nesten and FCEUD has a cheat console. And you can view the disassembly
to help you.
First check this disassembly around the play address. $8009 leads to $8585. So you want to check this area in the code
$8585 - $86AA. Write down all the Zero Page addresses that are used in the code. Yes, commonly $0 - $FF is used to switch the
tunes, however this isn't always so, be on the lookout. Write down these addresses.
So you narrowed down the list some. Run the game and open the memory viewer. Note the contents of the first address $FA.
So the title screen is playing, now go start the game and get on a level or something and see if the tunes changes. Usually
this number is going to be low, right in the range of 00 - 30 in hex about. This looks like it's the right address. The
next thing you have to do is find the right code and this isn't as hard as you think. Open FCEUD and set a read or write
breakpoint to $FA, try them both individually and note the code addresses it snaps on and write them down. This does take
some time however to deduce the right init address even with the right memory address.
Eventually you will see the following code and this is the right address.
$85F2> A5 FA: LDA $FA ; load tune to A, could be stuck on 0 $85F4> 09 80: ORA #$80 ; OR A $85F6> 85 FA: STA $FA ; store results to tune address $85F8> 0A: ASL A ; $85F9> 0A: ASL A ; $85FA> 0A: ASL A ; $85FB> A8: TAY ; $85FC> A2 07: LDX #$07 ; $85FE> B9 D1A2: LDA $A2D1,Y ; $8601> 95 F2: STA $F2,X ; $8603> 8D 7901: STA $0179 ; $8606> 88: DEY ; $8607> CA: DEX ; $8608> 10 F4: BPL $85FE
Use 85F2 for the init address and you find out it still doesn't play. Well, you have to initialize this address first. The
simple solution is to build a tune arrangement code and store to $FA and then jump to $85F2. Do it like this.
$C000 AA TAX $C001 BD 00C0 LDA $C009,X ; point to tune index $C004 85 FA STA $FA ; store to tune address $C006 4C F285 JMP $85F2 ; go to init .db tune index
Place the code wherever you want but you have to point the LDA $xxxx,X right on the tune index. Count the bytes from the start
of the code that you wrote and add that number to the address of the start of the code. Look and see the above code to see
how I did it.
So you learned one way of how to find the entry point to the init code and learned about memory addresses. And now that you
did all that work you should listen to the tune for a few moments.